
AddCSLuaFile( "cl_init.lua" );
AddCSLuaFile( "shared.lua" );

include( "shared.lua" ); 

local schedWanderAimless = ai_schedule.New( "WanderAimless" );
	  schedWanderAimless:EngTask( "TASK_WANDER", 10 );

local schedGoToEnemy = ai_schedule.New( "GoToEnemy" );
	  schedGoToEnemy:EngTask( "TASK_FACE_ENEMY", 0 );
	  schedGoToEnemy:EngTask( "TASK_GET_PATH_TO_ENEMY", 0 );
	  schedGoToEnemy:EngTask( "TASK_RUN_PATH", 0 );

	  
ZOMBIE_SEE_DISTANCE = 1000;
HECTIC_MODE = true;
	  
function ENT:Initialize()
   
    self:SetModel( "models/Zed/malezed_0" .. ( { 4, 6, 8 } )[math.random( 1, 3 )] .. ".mdl" )
  
    self:SetHullType( HULL_HUMAN );
    self:SetHullSizeNormal();
  
    self:SetSolid( SOLID_BBOX );
    self:SetMoveType( MOVETYPE_STEP );
  
    self:CapabilitiesAdd( CAP_MOVE_GROUND | CAP_ANIMATEDFACE | CAP_TURN_HEAD | CAP_USE_SHOT_REGULATOR | CAP_AIM_GUN );
		  
    self:SetMaxYawSpeed( 5000 );
    
    self.ChosenTarget = nil;
	self.NextThinkTime = CurTime() + .3;
	self.LastPos = self:GetPos();
	self.DeclareStuckTime = 0;
	self.LastJump = 0;
	
	self:SetHealth( 15 );
	
	self.NextSoundTime = 0;
	self.NextFindNewEnemy = 0;
	
	
end 

function ENT:AttemptToFindPlayer()

	local vec;

	if( HECTIC_MODE ) then
	
		vec = player.GetAll();
	
	else

		vec = ents.FindInSphere( self:GetPos(), ZOMBIE_SEE_DISTANCE );
	
	end
	
	local distances = { }
	local lowestdist = -1;
	local lowestdistindex = -1;
	
	for k, v in pairs( vec ) do
	
		if( v:IsPlayer() and v:Alive() and ( HECTIC_MODE or self:Visible( v ) ) ) then
		
			local dist = ( self.Entity:GetPos() - v:GetPos() ):Length();
			
			if( lowestdist == -1 or lowestdist > dist ) then
			
				lowestdist = dist;
				lowestdistindex = k;
				
			end
		
		end
	
	end
	
	if( lowestdistindex > -1 ) then

		self.ChosenTarget = vec[lowestdistindex];
		self:SetEnemy( vec[lowestdistindex] );
		self:UpdateEnemyMemory( vec[lowestdistindex], vec[lowestdistindex]:GetPos() );
		return;
		
	end

end

function ENT:HandleDeath()

	local ragdoll = ents.Create( "prop_ragdoll" );
	
	ragdoll:SetPos( self:GetPos() );
	ragdoll:SetKeyValue( "angles", self:GetAngles().p .. " " .. self:GetAngles().y .. " " .. self:GetAngles().r );
	ragdoll:SetModel( self:GetModel() );
	ragdoll:SetCollisionGroup( COLLISION_GROUP_WEAPON );
	
	ragdoll:Spawn();
	ragdoll:Activate();
	
	local vel = self:GetVelocity();
	
	for n = 1, 128 do
		
		local bone = ragdoll:GetPhysicsObjectNum( n );
		
		if( bone and bone:IsValid() ) then
		
			local bonepos, boneang = ragdoll:GetBonePosition( ragdoll:TranslatePhysBoneToBone( n ) );
			
			bone:SetPos( bonepos );
			bone:SetAngle( boneang );
			bone:AddVelocity( vel );
			
		end
		
	end
	
	timer.Simple( 3, function() if( ragdoll:IsValid() ) then ragdoll:Remove(); end end );
	
	self:SetSchedule( SCHED_FALL_TO_GROUND );
	
	self:Remove();

end

function ENT:HandleSound()

	if( CurTime() > self.NextSoundTime ) then
	
		self.Entity:EmitSound( "npc/zombie/zombie_pain" .. math.random( 1, 6 ) .. ".wav" );
	
		self.NextSoundTime = CurTime() + math.Rand( .5, 2 );
	
	end

end

function ENT:Think()

	if( self:Health() <= 0 ) then
	
		self:HandleDeath();
	
	end
	
	self:HandleSound();

end
	
function ENT:OnTakeDamage( dmg )

	self:SetHealth( self:Health() - dmg:GetDamage() );

end

function ENT:AttackTarget()

	local trace = { }
	
	trace.start = self:GetPos() + Vector( 0, 0, 58 );
	trace.endpos = trace.start + self:GetForward() * 50;
	trace.filter = self;
	
	local tr = util.TraceLine( trace );
	
	if( tr.Entity:IsValid() ) then
	
		if( tr.Entity:IsPlayer() ) then
		
			tr.Entity:TakeDamage( 15, self );
			tr.Entity:CallEvent( "HH" );
			tr.Entity:ViewPunch( Angle( math.random( -1, 1 ), math.random( -1, 1 ), 0 ) * 4 );
		
		end
	
	end

end

function ENT:SelectSchedule()

	if( GetConVarNumber( "ai_disabled" ) == 1 ) then return; end

	if( CurTime() < self.NextThinkTime ) then return; end
	self.NextThinkTime = CurTime() + .3;

	local vecsub = Vector( 0, 0, 0 );
	
	if( self.ChosenTarget and self.ChosenTarget:IsValid() ) then
	
		vecsub = ( self.ChosenTarget:GetPos() - self:GetPos() );
	
	end
	
	if( CurTime() > self.NextFindNewEnemy ) then
	
		self:AttemptToFindPlayer();
		self.NextFindNewEnemy = CurTime() + .5;
	
	end

	if( not self.ChosenTarget or not self.ChosenTarget:IsValid() or not self.ChosenTarget:Alive() or ( vecsub:Length() > ZOMBIE_SEE_DISTANCE and not HECTIC_MODE ) ) then
		self.ChosenTarget = nil;
		self:StartSchedule( schedWanderAimless );
	
		return;
	end

	self:SetEnemy( self.ChosenTarget );
	self:UpdateEnemyMemory( self.ChosenTarget, self.ChosenTarget:GetPos() );
	
	local speedboost = vecsub:Normalize() * 300;
	
	if( self.ChosenTarget:GetPos().z > self:GetPos().z ) then
	
		if( CurTime() - self.LastJump < 2 ) then
		
			speedboost.z = 0;
			
		else
			
			self.LastJump = CurTime();
			speedboost.z = speedboost.z;
			speedboost.x = speedboost.x * .6;
			speedboost.y = speedboost.y * .6;
			
		end
		
	else
		
		speedboost.z = 0;
	
	end
	
	self:SetVelocity( Vector( speedboost.x, speedboost.y, speedboost.z ) );
	
	local normalroutine = true;
	
	if( ( self:GetPos() - self.ChosenTarget:GetPos() ):Length() < 50 ) then
		
		local schedAttackAnim = ai_schedule.New( "AttackAnim" );
		schedAttackAnim:EngTask( "TASK_FACE_ENEMY", 0 );
 		schedAttackAnim:EngTask( "TASK_GET_PATH_TO_ENEMY", 0 );
  		schedAttackAnim:EngTask( "TASK_RUN_PATH", 0 );
		schedAttackAnim:AddTask( "AttackAnim", { Name = "swing", Speed = 2 } )
		
		self:StartSchedule( schedAttackAnim );
		self:AttackTarget();
		normalroutine = false;
	
		
	end
	
	if( normalroutine ) then
	
		self:StartSchedule( schedGoToEnemy );
	
	end
	
	self.LastPos = self:GetPos();

	
end



 /*---------------------------------------------------------
   Task: PlaySequence
  //-------------------------------------------------------*/
  function ENT:TaskStart_AttackAnim( data )
   
   local SequenceID = data.ID
   
   if ( data.Name ) then SequenceID = self:LookupSequence( data.Name ) end
 
   	SequenceID = self:SelectWeightedSequence( 277 );
   	data.Speed = nil;

   self:ResetSequence( SequenceID )
   self:SetNPCState( NPC_STATE_SCRIPT )
   
   local Duration = self:SequenceDuration()
   
   if ( data.Speed && data.Speed > 0 ) then
   
   SequenceID = self:SetPlaybackRate( data.Speed )
   Duration = Duration / data.Speed
   
   end
   
   self.TaskSequenceEnd = CurTime() + Duration
   
  end
  /*---------------------------------------------------------*/
  function ENT:Task_AttackAnim( data )
   
   // Wait until sequence is finished
   if ( CurTime() < self.TaskSequenceEnd ) then return end
  
   self:TaskComplete()
   self:SetNPCState( NPC_STATE_NONE )
   
   // Clean up
   self.TaskSequenceEnd = nil
   
  end 

